package com.lizemin.jingdongmall.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author lzm
 * @date 2025/3/28 21:04
 * @description
 */
@Service
public class RedisLockService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 尝试获取锁
     *
     * @param key     锁的key
     * @param uuid    锁的版本号
     * @param timeout 锁的超时时间
     * @param timeUnit 时间单位
     * @return 获取锁的结果 true 获取锁成功，false 获取锁失败
     */
    public boolean tryLock(String key, String uuid, long timeout, TimeUnit timeUnit) {
        return Boolean.TRUE.equals(redisTemplate.execute((RedisCallback<Boolean>)
                connection -> connection.set(key.getBytes(StandardCharsets.UTF_8),
                        uuid.getBytes(StandardCharsets.UTF_8),
                        Expiration.from(timeout, timeUnit), RedisStringCommands.SetOption.SET_IF_ABSENT)));
    }

    /**
     * 释放锁
     *
     * @param key 锁的key
     * @param uuid 锁的唯一标识
     * @return 释放锁的结果 true 释放锁成功，false 释放锁失败
     */
    public boolean releaseLock(String key, String uuid) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute((RedisCallback<Long>) connection ->
                connection.eval(script.getBytes(StandardCharsets.UTF_8),
                        ReturnType.INTEGER,
                        1,
                        key.getBytes(StandardCharsets.UTF_8),
                        uuid.getBytes(StandardCharsets.UTF_8)));
        if (Objects.isNull(result)) {
            return false;
        }
        return result > 0;
    }

}
